<?php

namespace App\Models;

use App\Common\Traits\HasOrders;
use Carbon\Carbon;
use Illuminate\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;
use Tymon\JWTAuth\Contracts\JWTSubject;

class Agent extends Base implements JWTSubject, AuthenticatableContract
{
    /**
     * HasOrders 是关联了团队和合伙人的订单, 自营订单还是直接根据 orders 里的 agent_id 来就行
     */
    use HasOrders, Authenticatable, SoftDeletes;

    /**
     * 等级: 群主
     */
    const LEVEL_GROUP_OWNER = 1;

    /**
     * 等级: 精英
     */
    const LEVEL_ELITE = 2;

    /**
     * 等级: 大咖
     */
    const LEVEL_STRONGMAN = 3;

    const LEVEL_MAP = [
        self::LEVEL_GROUP_OWNER => [
            'id' => 'group_owner',
            'text' => '群主',
            'employment' => [
                'self' => 10000 * 100, // 单位 分
                'team' => 20000 * 100
            ]
        ],
        self::LEVEL_ELITE => [
            'id' => 'elite',
            'text' => '精英',
            'employment' => [
                'self' => 80000 * 100,
                'team' => 200000 * 100
            ]
        ],
        self::LEVEL_STRONGMAN => [
            'id' => 'strongman',
            'text' => '大咖',
            'employment' => [
                'self' => 1, // 到顶级了
                'team' => 1
            ]
        ]
    ];

    /**
     * 培养奖: 开启
     */
    const CULTIVATION_AWARD_OPEN = 1;

    /**
     * 培养奖: 关闭
     */
    const CULTIVATION_AWARD_CLOSE = 2;

    /**
     * 培养奖
     */
    const CULTIVATION_AWARD_MAP = [
        self::CULTIVATION_AWARD_OPEN => '开启',
        self::CULTIVATION_AWARD_CLOSE => '关闭',
    ];

    /**
     * 培养奖金额 (单位 分)
     */
    const CULTIVATION_AWARD = 100;

    /**
     * 订单中间表 身份: 代理商
     */
    const AGENT_ORDER_IDENTITY_AGENT = 1;

    /**
     * 订单中间表 身份: 团队
     */
    const AGENT_ORDER_IDENTITY_TEAM = 2;

    /**
     * 订单中间表 身份: 合伙人
     */
    const AGENT_ORDER_IDENTITY_PARTNER = 3;

    /**
     * 性别: 男
     */
    const GENDER_MALE = 1;

    /**
     * 性别: 女
     */
    const GENDER_FEMALE = 2;

    /**
     * 性别对照 Map
     */
    const GENDER_CN_MAP = [
        self::GENDER_MALE => '男',
        self::GENDER_FEMALE => '女',
    ];

    protected $hidden = [
        'password',
    ];

    protected $casts = [
        'created_at' => 'datetime:Y-m-d H:i:s',
        'updated_at' => 'datetime:Y-m-d H:i:s',
        'deleted_at' => 'datetime:Y-m-d H:i:s',
        'is_agree_user_agreement' => 'bool',
        'last_time_of_login' => 'datetime:Y-m-d H:i:s',
    ];

    // 存放 summary model "缓存"
    private $summaryModelCache = [];

    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    public function getJWTCustomClaims()
    {
        return [];
    }

    /**
     * 代理自营订单
     */
    public function selfSupportOrders()
    {
        return $this->hasMany(Order::class, 'agent_id');
    }

    /**
     * 标签
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function labels()
    {
        return $this->belongsToMany(AgentLabel::class, 'agent_has_label', 'agent_id', 'label_id');
    }

    /**
     * 直接邀请人
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function directInviter()
    {
        return $this->belongsTo(Agent::class, 'direct_inviter');
    }

    /**
     * 间接邀请人
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function indirectInviter()
    {
        return $this->belongsTo(Agent::class, 'indirect_inviter');
    }

    /**
     * 微信信息
     * @return \Illuminate\Database\Eloquent\Relations\HasOne
     */
    public function wechat()
    {
        return $this->hasOne(WechatUser::class);
    }

    /**
     * 合伙人身份
     * @return \Illuminate\Database\Eloquent\Relations\HasOne
     */
    public function partnerIdentity()
    {
        return $this->hasOne(Partner::class);
    }

    /**
     * 所属合伙人
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function affiliatedPartner()
    {
        return $this->belongsTo(Agent::class, 'affiliated_partner');
    }

    /**
     * 收支明细
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function revenue()
    {
        return $this->hasMany(AgentRevenue::class);
    }

    /**
     * 用户晋升记录
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function promoted()
    {
        return $this->hasMany(AgentPromoted::class);
    }

    /**
     * 用户站内信
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function websiteLetter()
    {
        return $this->hasMany(AgentWebsiteLetter::class);
    }

    public function province()
    {
        return $this->hasOne(Province::class, 'code', 'province');
    }

    public function city()
    {
        return $this->hasOne(City::class, 'code', 'city');
    }

    public function area()
    {
        return $this->hasOne(Area::class, 'code', 'area');
    }

    /**
     * 每日汇总
     */
    public function dailySummary()
    {
        return $this->hasMany(IndicatorsDailySummary::class);
    }

    /**
     * 每月汇总
     */
    public function monthlySummary()
    {
        return $this->hasMany(IndicatorsMonthlySummary::class);
    }

    /**
     * 奖励
     */
    public function rewards()
    {
        return $this->hasMany(AgentReward::class);
    }

    /**
     * 提现记录
     */
    public function withdrawRecords()
    {
        return $this->hasMany(AgentWithdrawRecord::class);
    }

    /**
     * 提现卡注册记录
     */
    public function withdrawAccountReg()
    {
        return $this->hasMany(AgentWithdrawAccountReg::class);
    }

    /**
     * 每日统计
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function indicatorsDailySummary()
    {
        return $this->hasMany(IndicatorsDailySummary::class);
    }

    /**
     * 每月统计
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function indicatorsMonthlySummary()
    {
        return $this->hasMany(IndicatorsMonthlySummary::class);
    }

    /**
     * 充值订单
     */
    public function topUpOrder()
    {
        return $this->hasMany(AgentTopUpOrder::class);
    }

    /**
     * 厦门银行(身份)账号
     * @return \Illuminate\Database\Eloquent\Relations\HasOne
     */
    public function xiamenBankAccount()
    {
        return $this->hasOne(AgentXiamenBankAccount::class);
    }

    /**
     * 当前级别身份
     * @return array
     */
    public function currentLevelIdentity()
    {
        return self::LEVEL_MAP[$this->level];
    }

    /**
     * 增加 / 减少 用户(可提现)余额
     * @param $balance
     * @param $type
     * @param null $related
     * @param string $method
     * @param \DateTimeInterface|null $date
     * @return AgentRevenue|Base|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|null
     */
    public function incrementOrDecrementBalance($balance, $type, $related = null, $method = 'increment', \DateTimeInterface $date = null)
    {
        if ($balance <= 0) {
            return null;
        }
        $date === null && $date = Carbon::now();

        // 奖金操作,操作额外累计奖金总数
        if (in_array($type, AgentRevenue::TYPE_REWARD_LIST, true)) {
            $this->{$method}('reward', $balance);
            // FIXME 2020-10-25 00:34:47 个人统计里团队是没有的奖金, 以下内容作废
            // 自己(团队)
            $this->incrementOrDecrementTeamReward($balance, $method, $date);
            // 上级(团队) FIXME 这里涉及的字段 不需要,但是需要 RemoveTeamCommission 跑历史数据
            $this->directInviter && $this->directInviter->incrementOrDecrementTeamReward($balance, $method, $date);
            $this->indirectInviter && $this->indirectInviter->incrementOrDecrementTeamReward($balance, $method, $date);
            // FIXME 2020-10-25 00:34:47 个人统计里团队是没有的奖金, 内容作废 [END]
            // 合伙人累计
            if ($this->affiliatedPartner !== null && $this->affiliatedPartner->partnerIdentity) {
                $this->affiliatedPartner->partnerIdentity->incrementOrDecrementReward($balance, $method, $date);
            }
            // 日月累计
            $this->cumulativeDailySummary(SummaryBase::TYPE_AGENT, $date, $method, 0, 0, 0, 0, $balance);
            $this->cumulativeMonthlySummary(SummaryBase::TYPE_AGENT, $date, $method, 0, 0, 0, 0, $balance);
        }
        // 佣金操作,操作额外累计佣金总数
        if (in_array($type, AgentRevenue::TYPE_COMMISSION_LIST, true)) {
            $this->{$method}('commission', $balance);
            // FIXME 2020-10-25 00:34:47 个人统计里团队是没有佣金的, 以下内容作废
            // 自己(团队)
            $this->incrementOrDecrementTeamCommission($balance, $method, $date);
            // 上级(团队) FIXME 这里涉及的字段 不需要,但是需要 RemoveTeamCommission 跑历史数据 2020-10-25 00:33:48 直接作废即可不用跑脚本也行,代码可以先不去掉,但是后面上了更改代理层级关系,脚本就不行了,故这里直接作废
            $this->directInviter && $this->directInviter->incrementOrDecrementTeamCommission($balance, $method, $date);
            $this->indirectInviter && $this->indirectInviter->incrementOrDecrementTeamCommission($balance, $method, $date);
            // FIXME 2020-10-25 00:34:47 个人统计里团队是没有佣金的, 内容作废 [END]
            // 合伙人累计
            if ($this->affiliatedPartner !== null && $this->affiliatedPartner->partnerIdentity) {
                $this->affiliatedPartner->partnerIdentity->incrementOrDecrementCommission($balance, $method, $date);
            }
            // 日月累计
            $this->cumulativeDailySummary(SummaryBase::TYPE_AGENT, $date, $method, 0, 0, 0, $balance);
            $this->cumulativeMonthlySummary(SummaryBase::TYPE_AGENT, $date, $method, 0, 0, 0, $balance);
        }

        $this->{$method}('balance', $balance);

        // 账单明细
        $agentRevenue = $related === null ? AgentRevenue::query() : $related->agentRevenues();
        return $agentRevenue->create([
            'agent_id' => $this->getKey(),
            'revenues_type' => ['increment' => AgentRevenue::REVENUES_TYPE_INCOME, 'decrement' => AgentRevenue::REVENUES_TYPE_EXPENDITURE][$method],
            'type' => $type,
            'amount' => $balance,
            'balance' => $this['balance'],
            'unextractable_balance' => $this['unextractable_balance'],
            'created_at' => $date
        ]);
    }

    /**
     * 增加 / 减少 用户(不可提现)余额
     * @param $unextractableBalance
     * @param $type
     * @param null $related
     * @param string $method
     * @return AgentRevenue|Base|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|null
     */
    public function incrementOrDecrementUnextractableBalance($unextractableBalance, $type, $related = null, $method = 'increment')
    {
        if ($unextractableBalance <= 0) {
            return null;
        }

        $this->{$method}('unextractable_balance', $unextractableBalance);

        // 账单明细
        $agentRevenue = $related === null ? AgentRevenue::query() : $related->agentRevenues();
        return $agentRevenue->create([
            'agent_id' => $this->getKey(),
            'revenues_type' => ['increment' => AgentRevenue::REVENUES_TYPE_INCOME, 'decrement' => AgentRevenue::REVENUES_TYPE_EXPENDITURE][$method],
            'type' => $type,
            'amount' => $unextractableBalance,
            'balance' => $this['balance'],
            'unextractable_balance' => $this['unextractable_balance']
        ]);
    }

    /**
     * 增加 / 减少 用户提现金额
     * @param $column
     * @param $amount
     * @param $method
     */
    public function incrementOrDecrementAmountInWithdrawal($amount, $method = 'increment')
    {
        $amount > 0 && $this->{$method}('amount_in_withdrawal', $amount);
    }

    /**
     * 增加 / 减少 用户(团队)奖金总和
     * @param $amount
     * @param string $method
     * @param \DateTimeInterface|null $date
     */
    public function incrementOrDecrementTeamReward($amount, $method = 'increment', \DateTimeInterface $date = null)
    {
        $amount > 0 && $this->{$method}('team_reward', $amount);

        // 日月累计
        $date === null && $date = Carbon::now();
        $amount > 0 && $this->cumulativeDailySummary(SummaryBase::TYPE_TEAM, $date, $method, 0, 0, 0, 0, $amount);
        $amount > 0 && $this->cumulativeMonthlySummary(SummaryBase::TYPE_TEAM, $date, $method, 0, 0, 0, 0, $amount);
    }

    /**
     * 增加 / 减少 用户(团队)佣金总和
     * @param $amount
     * @param string $method
     * @param \DateTimeInterface|null $date
     */
    public function incrementOrDecrementTeamCommission($amount, $method = 'increment', \DateTimeInterface $date = null)
    {
        $amount > 0 && $this->{$method}('team_commission', $amount);

        // 日月累计
        $date === null && $date = Carbon::now();
        $amount > 0 && $this->cumulativeDailySummary(SummaryBase::TYPE_TEAM, $date, $method, 0, 0, 0, $amount);
        $amount > 0 && $this->cumulativeMonthlySummary(SummaryBase::TYPE_TEAM, $date, $method, 0, 0, 0, $amount);
    }

    /**
     * 增加用户(个人)业绩 和 订单数
     * @param $amount
     * @param string $method
     * @param \DateTimeInterface|null $date
     * @param int $numberOfOrder
     */
    public function incrementOrDecrementPerformanceByOrder($amount, $method = 'increment', \DateTimeInterface $date = null, $numberOfOrder = 1)
    {
        $amount > 0 && $this->{$method}('personal_performance', $amount);
        $numberOfOrder > 0 && $this->{$method}('number_of_personal_order', $numberOfOrder);

        // 日月累计
        $date === null && $date = Carbon::now();
        $amount > 0 && $this->cumulativeDailySummary(SummaryBase::TYPE_AGENT, $date, $method, $amount, $numberOfOrder);
        $amount > 0 && $this->cumulativeMonthlySummary(SummaryBase::TYPE_AGENT, $date, $method, $amount, $numberOfOrder);

        // 合伙人累计
        if ($this->affiliatedPartner !== null
            && $this->affiliatedPartner->partnerIdentity
            && $amount > 0) {

            $this->affiliatedPartner->partnerIdentity->incrementOrDecrementPerformanceByOrder($amount, $method, $date, $numberOfOrder);
        }
    }

    /**
     * 增加用户(团队)业绩 和 订单数
     * @param $amount
     * @param string $method
     * @param \DateTimeInterface|null $date
     * @param int $numberOfOrder
     */
    public function incrementOrDecrementTeamPerformance($amount, $method = 'increment', \DateTimeInterface $date = null, $numberOfOrder = 1)
    {
        $amount > 0 && $this->{$method}('team_performance', $amount);
        $numberOfOrder > 0 && $this->{$method}('number_of_team_order', $numberOfOrder);

        // 日月累计
        $date === null && $date = Carbon::now();
        $amount > 0 && $this->cumulativeDailySummary(SummaryBase::TYPE_TEAM, $date, $method, $amount, $numberOfOrder);
        $amount > 0 && $this->cumulativeMonthlySummary(SummaryBase::TYPE_TEAM, $date, $method, $amount, $numberOfOrder);
    }

    /**
     * 增加 / 减少 用户累计收入
     * @param $amount
     * @param string $method
     * @param $date
     */
    public function incrementOrDecrementIncome($amount, $method = 'increment', \DateTimeInterface $date = null)
    {
        $this->{$method}('income', $amount);

        // 日月累计
        $date === null && $date = Carbon::now();
        $amount > 0 && $this->cumulativeDailySummary(SummaryBase::TYPE_AGENT, $date, $method, 0, 0, $amount);
        $amount > 0 && $this->cumulativeMonthlySummary(SummaryBase::TYPE_AGENT, $date, $method, 0, 0, $amount);

        // 合伙人累计
        if ($this->affiliatedPartner !== null
            && $this->affiliatedPartner->partnerIdentity
            && $amount > 0) {
            $this->affiliatedPartner->partnerIdentity->incrementOrDecrementIncome($amount, $method, $date);
        }
    }

    /**
     * 增加 / 减少 用户团队累计收入
     * @param $amount
     * @param string $method
     * @param \DateTimeInterface|null $date
     */
    public function incrementOrDecrementTeamIncome($amount, $method = 'increment', \DateTimeInterface $date = null)
    {
        $this->{$method}('team_income', $amount);

        // 日月累计
        $date === null && $date = Carbon::now();
        $this->cumulativeDailySummary(SummaryBase::TYPE_TEAM, $date, $method, 0, 0, $amount);
        $this->cumulativeMonthlySummary(SummaryBase::TYPE_TEAM, $date, $method, 0, 0, $amount);
    }

    /**
     * 申请晋升
     */
    public function applyForPromotion()
    {
        AgentPromoted::query()->firstOrCreate([
            'agent_id' => $this['id'],
            'status' => AgentPromoted::STATUS_TO_BE_VERIFIED
        ], [
            'level' => $this['level'],
            'personal_performance' => $this['personal_performance'],
            'team_performance' => $this['team_performance'],
            'check_date' => Carbon::today()->addDays(3)->format('Y-m-d')
        ]);
    }

    /**
     * 累计每日汇总
     * @param $type integer 用户身份类型
     * @param \DateTimeInterface $day
     * @param string $method
     * @param int $performance
     * @param int $order
     * @param int $income
     * @param int $commission
     * @param int $reward
     */
    public function cumulativeDailySummary(
        $type, \DateTimeInterface $day, $method = 'increment',
        $performance = 0, $order = 0, $income = 0, $commission = 0, $reward = 0
    )
    {
        // 解决多次查询的优化, 这里"缓存"起来
        $dateKey = $day->format('Y-m-d');
        $dateSummaryKey = sprintf('agent_id_%d_type_%d_date_%s', $this->id, $type, $dateKey);
        if (isset($this->summaryModelCache[$dateSummaryKey])) {
            $model = $this->summaryModelCache[$dateSummaryKey];
        } else {
            $model = $this->dailySummary()->firstOrCreate(['type' => $type, 'day' => $dateKey]);
            $this->summaryModelCache[$dateSummaryKey] = $model;
        }

        // 本次请求周期里创建的, 并且是扣减操作, 则不进行扣减, 因为默认值为零, 且不允许负值
        // 出现场景: 后面下级代理变更为当前代理后, 下面的代理取消订单或者售后时, 找到的当前代理去扣减, 但是之前并没有下级
        if ($model->wasRecentlyCreated && $method === 'decrement') {
            return;
        }

        try {
            $performance > 0 && $model->{$method}('performance_summary', $performance);
            $order > 0 && $model->{$method}('order_summary', $order);
            $commission > 0 && $model->{$method}('commission_summary', $commission);
            $reward > 0 && $model->{$method}('reward_summary', $reward);
            $income > 0 && $model->{$method}('income_summary', $income);
        } catch (\Exception $e) {
            \Log::info(sprintf('扣减日统计汇总异常, 不影响逻辑: %s (%s [%d])', $e->getMessage(), $e->getFile(), $e->getLine()));
        }
    }

    /**
     * 累计每月汇总
     * @param $type integer 用户身份类型
     * @param \DateTimeInterface $month
     * @param string $method
     * @param int $performance
     * @param int $order
     * @param int $income
     * @param int $commission
     * @param int $reward
     */
    public function cumulativeMonthlySummary(
        $type, \DateTimeInterface $month, $method = 'increment',
        $performance = 0, $order = 0, $income = 0, $commission = 0, $reward = 0
    )
    {
        // 解决多次查询的优化, 这里"缓存"起来
        $dateKey = (int) $month->format('Ym');
        $dateSummaryKey = sprintf('agent_id_%d_type_%d_date_%d', $this->id, $type, $dateKey);
        if (isset($this->summaryModelCache[$dateSummaryKey])) {
            $model = $this->summaryModelCache[$dateSummaryKey];
        } else {
            $model = $this->monthlySummary()->firstOrCreate(['type' => $type, 'month' => $dateKey]);
            $this->summaryModelCache[$dateSummaryKey] = $model;
        }

        // 本次请求周期里创建的, 并且是扣减操作, 则不进行扣减, 因为默认值为零, 且不允许负值
        // 出现场景: 后面下级代理变更为当前代理后, 下面的代理取消订单或者售后时, 找到的当前代理去扣减, 但是之前并没有下级
        if ($model->wasRecentlyCreated && $method === 'decrement') {
            return;
        }

        // 这里的 catch 是上面的场景, 不在本次请求周期创建的, 此时也会扣出负数来, 捕获一下
        try {
            $performance > 0 && $model->{$method}('performance_summary', $performance);
            $order > 0 && $model->{$method}('order_summary', $order);
            $commission > 0 && $model->{$method}('commission_summary', $commission);
            $reward > 0 && $model->{$method}('reward_summary', $reward);
            $income > 0 && $model->{$method}('income_summary', $income);
        } catch (\Exception $e) {
            \Log::info(sprintf('扣减月统计汇总异常, 不影响逻辑: %s (%s [%d])', $e->getMessage(), $e->getFile(), $e->getLine()));
        }
    }

    /**
     * 计算扣减(可提现 不可提现)余额 单位全是分
     * @param int $spend 花费金额
     * @param int $balance 当前可提现余额
     * @param int $unextractableBalance 当前不可提现余额
     * @param bool $forceDeductionBalance 强制扣减 (不管是否会负余额)
     * @return int[]\
     */
    public static function deductionBalance($spend = 0, &$balance = 0, &$unextractableBalance = 0, $forceDeductionBalance = false)
    {
        if ($spend <= 0) {
            return [0, 0];
        }

        $unextractableBalancePaid = 0;
        $balancePaid = 0;
        if ($unextractableBalance - $spend >= 0) {
            $unextractableBalancePaid = $spend; // 使用了不可提现金额
            $unextractableBalance -= $spend; // 扣减不可提现金额
        } else {
            if ($unextractableBalance > 0) {
                $unextractableBalancePaid = $unextractableBalance; // 使用了不可提现金额
                $spend -= $unextractableBalance; // 扣减不可提现金额, 剩余的商品总价
                $unextractableBalance = 0;
            }

            $balancePaid = $spend;
            $balance -= $spend;
            if ($balance < 0 && !$forceDeductionBalance) {
                throw new \Symfony\Component\HttpKernel\Exception\HttpException(400, '余额不足');
            }
        }

        return [$balancePaid, $unextractableBalancePaid];
    }

    /**
     * 生成邀请码
     * @return string
     */
    public static function generateInvitationCode()
    {
        return strtoupper(Str::random(6));
    }
}
